#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>

int main(int argc,const char *argv[])
{
	//定义两个套接字描述符，用于描述服务器和客户端套接字
	int server_fd,client_fd;
	//定义变量pid用于区分父子进程
	pid_t pid;
	//一些接口函数返回值，用于判断接口函数是否执行成功
	int ret;
	//收发数据缓冲区，由于父子进程内存空间独立，故可使用同一个缓冲区
	char buf[64] = "";
	//用于描述服务器和客户端的网络信息结构体
	struct sockaddr_in server_addr,client_addr;
	//描述网络信息结构体变量大小的变量
	socklen_t addrlen = sizeof(struct sockaddr_in);

	//1、相当于买了个手机
	//@AF_INET：协议族
	//@SOCK_STREAM：流式套接字，专用于tcp通信
	//返回值：服务器套接字描述符
	server_fd = socket(AF_INET, SOCK_STREAM, 0);
	if(server_fd == -1)
	{
		perror("fail to socket");
		return -1;
	}
	//2、相当于办了张手机卡
	//清空这张手机卡
	memset(&server_addr,0,sizeof(server_addr));
	//填写协议族，端口号，ip地址，注意大小端转换
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(atoi(argv[2]));
	server_addr.sin_addr.s_addr = inet_addr(argv[1]);

	//3、相当于插卡
	//@server_fd：服务器套接字描述符
	//@server_addr：服务器网络信息结构体，注意强制类型转化
	//@server_addr:网络信息结构体的大小
	ret = bind(server_fd, (struct sockaddr *)&server_addr,sizeof(server_addr));
	if(ret == -1)
	{
		perror("fail to bind");
		return -1;
	}
	//4、相当于开机
	//开机监听套接字
	//第一个参数为服务器套接字描述符，第二个参数为监听队列长度，最大为5
	ret = listen(server_fd, 5);
	if(ret == -1)
	{
		perror("fail to listen");
		return -1;
	}
	//5、相当于接电话
	//主要目的是获取描述客户端的套接字描述符，通过操作客户端套接字描述符，
	//可完成相应的收发功能
	//@server_fd：服务器套接字描述符
	//@client_addr：用于存储客户端的网络信息
	//@addrlen:客户端网络信息结构体的长度，注意传地址
	client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &addrlen);
	if(client_fd == -1)
	{
		perror("fail to accept");
		return -1;
	}
	//打印一下客户端的网络信息
	printf("client addr:%s\n",inet_ntoa(client_addr.sin_addr));
	//通过fork函数创建子进程
	pid = fork();
	//当fork返回值pid大于0时，为父进程
	if(pid > 0)
	{
		//父进程内进行tcp的接收工作
		while(1)
		{
			//6、通话，此处为听
			//通过recv函数接收客户端数据
			//@client_fd:客户端套接字描述符
			//@buf：用于接收客户端数据的数组
			//@64：尝试接收64字节
			//@0:阻塞接收
			ret = recv(client_fd, buf, 64, 0);
			//ret大于0代表收到了数据
			if(ret > 0)
			{
				printf("%s\n",buf);
				memset(buf,0,64);
			}
			//ret小于0代表接收出错
			else if(ret < 0)
			{
				perror("fail to recv\n");
				close(client_fd);
				close(server_fd);
				return -1;
			}
			//ret等于0代表对端退出
			else
			{
				printf("peer exit\n");
				close(client_fd);
				close(server_fd);
				return 0;
			}
		}
	}	
	//pid等于0代表子进程
	else if(pid == 0)
	{
		//子进程主要完成tcp的发送工作
		while(1)
		{
			scanf("%s",buf);
			getchar();
			//6、通话，此处为说
			//通过send函数完成tcp的发送功能
			//@client_fd:客户端套接字描述符
			//@buf:存放将要发送的数据的数组
			//@strlen(buf)：要发送的数据的长度
			//@0:阻塞发送
			send(client_fd, buf, strlen(buf), 0);
			memset(buf,0,64);
		}
	}
	else
	{
		printf("err\n");
		return -1;
	}
	
	return 0;
}
